package org.lee.nio.tcp;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;

/**
 * @author jackielee
 * @version 1.0
 * @description NIOServer
 * @date 2021/3/8
 **/
public class NIOServer {
    // 通道管理器
    private Selector selector;

    public void initServer(int port) throws IOException {
        // 获得一个serverSocket通道
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        // 设置通道为非阻塞
        serverChannel.configureBlocking(false);
        // 将通道对应serverSocket绑定到port端口
        serverChannel.socket().bind(new InetSocketAddress(port));
        // 获取一个通道管理
        selector = Selector.open();
        // 通道管理器与通道绑定，并注册OP_ACCEPT事件，当该事件到达时，selector.select()会返回，如该事件没到达selector.select()会一直阻塞
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    /**
     * 轮询方式监听selector是否要处理事件
     */
    public void listen() throws IOException {
        System.out.println("服务器启动成功！");
        while (true) {
            // 当注册事件到达时，返回，否则会一直阻塞
            selector.select();
            Iterator ite = selector.selectedKeys().iterator();
            while (ite.hasNext()) {
                SelectionKey key = (SelectionKey) ite.next();
                // 删除已选key，防重复处理
                ite.remove();
                // 客户端请求连接事件
                if (key.isAcceptable()) {
                    ServerSocketChannel server = (ServerSocketChannel) key.channel();
                    SocketChannel channel = server.accept(); // 客户端通道
                    channel.configureBlocking(false); // 非阻塞
                    // 发送给客户端
                    channel.write(ByteBuffer.wrap("hello, 客户端，欢迎~~~".getBytes()));
                    // 为接收客户端信息，给通道设置读权限
                    channel.register(selector, SelectionKey.OP_READ);
                } else if (key.isReadable()) // 可读事件
                {
                    read(key);
                }
            }
        }
    }

    /**
     * 读取客户端消息
     *
     * @param key
     */
    private void read(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        channel.read(buffer);
        byte[] data = buffer.array();
        String msg = new String(data).trim();
        System.out.println("收到:" + msg);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String msgSent = "[" + sdf.format(new Date()) + "]" + msg;
        ByteBuffer outBuffer = ByteBuffer.wrap(msgSent.getBytes());
        channel.write(outBuffer);
    }

    public static void main(String[] args) throws IOException {
        NIOServer server = new NIOServer();
        server.initServer(8080);
        server.listen();
    }
}
